package org.shagin.jfin.ui.budget.item;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.shagin.jfin.core.CoreUtils;
import org.shagin.jfin.core.Debug;
import org.shagin.jfin.core.ui.JJDialog;
import org.shagin.jfin.core.ui.UIUtils;
import org.shagin.jfin.data.model.OperationRule;
import org.shagin.jfin.data.model.OperationRuleAction;
import org.shagin.jfin.date.cond.IDateCondition;
import org.shagin.jfin.ui.UI;
import org.shagin.jfin.ui.date.DateRangeEvent;
import org.shagin.jfin.ui.date.DateRangeListener;
import org.shagin.jfin.ui.date.JDateRange;
import org.shagin.jfin.ui.date.cond.DateConditionEvent;
import org.shagin.jfin.ui.date.cond.DateConditionListener;
import org.shagin.jfin.ui.date.cond.DateConditionPane;
import org.shagin.jfin.ui.date.cond.JDailyPane;
import org.shagin.jfin.ui.date.cond.JExactDatePane;
import org.shagin.jfin.ui.date.cond.JMonthlyPane;
import org.shagin.jfin.ui.date.cond.JNeverPane;
import org.shagin.jfin.ui.date.cond.JWeeklyPane;
import org.shagin.jfin.ui.date.cond.JYearlyPane;
import org.shagin.jfin.ui.money.JMoneyPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class JOperationRuleActionPane.
 */
@SuppressWarnings("serial")
public class JOperationRuleActionPane extends JPanel {

    private static final Logger log = LoggerFactory
            .getLogger(JOperationRuleActionPane.class);

    private OperationRuleAction operationRuleAction;
    private JTabbedPane tabbedPane = new JTabbedPane();
    private JLabel labelDateCondition = new JLabel("?");
    private JDateRange dateRange = new JDateRange();
    private JMoneyPane moneyPane = new JMoneyPane();
    private Map<Class<?>, DateConditionPane<?>> dateCondPanes = new HashMap<Class<?>, DateConditionPane<?>>();
    private DateConditionListener dateConditionListener;
    private DateRangeListener dateRangeListener;

    /**
     * Instantiates a new j operation rule action pane.
     * 
     * @param operationRuleAction the operation rule action
     */
    public JOperationRuleActionPane(OperationRuleAction operationRuleAction) {
        super();
        CoreUtils.checkNotNull(operationRuleAction, "operationRuleAction");
        initListeners();
        setLayout(new BorderLayout());
        add(createHeader(), BorderLayout.NORTH);
        add(createTabbedPane(), BorderLayout.CENTER);
        add(createButtonsPanel(), BorderLayout.SOUTH);
        setOperationRuleAction(operationRuleAction);
        addListeners();
    }

    /**
     * Inits the listeners.
     */
    private void initListeners() {
        dateConditionListener = new DateConditionListener() {

            @Override
            public void dateConditionChanged(DateConditionEvent event) {
                Debug.traceEnter(log);
                updateDateConditionLabel(event.getDateCondition());
            }
        };

        dateRangeListener = new DateRangeListener() {

            @Override
            public void dateRangeChanged(DateRangeEvent e) {
                Debug.traceEnter(log);
                IDateCondition dc = getSelectedDateCondition();
                dc.getRange().assign(e.getDateRange());
                updateDateConditionLabel(dc);

            }
        };
    }

    /**
     * Adds the listeners.
     */
    private void addListeners() {
        dateRange.addChangeListener(dateRangeListener);
    }

    /**
     * Creates the header.
     * 
     * @return the j panel
     */
    private JPanel createHeader() {
        JPanel panel = new JPanel(new BorderLayout(UI.HGAP, UI.VGAP));

        JPanel nothPanel = new JPanel(new FlowLayout(FlowLayout.CENTER,
                UI.HGAP, UI.VGAP));
        nothPanel.add(labelDateCondition);

        JPanel centerPanel = new JPanel(new BorderLayout(UI.HGAP, UI.VGAP));
        centerPanel.add(dateRange, BorderLayout.WEST);
        centerPanel.add(moneyPane, BorderLayout.EAST);

        panel.add(nothPanel, BorderLayout.NORTH);
        panel.add(centerPanel, BorderLayout.CENTER);
        return panel;
    }

    /**
     * Creates the tabbed pane.
     * 
     * @return the j tabbed pane
     */
    private JTabbedPane createTabbedPane() {
        addTab(new JDailyPane());
        addTab(new JExactDatePane());
        addTab(new JWeeklyPane());
        addTab(new JMonthlyPane());
        addTab(new JYearlyPane());
        addTab(new JNeverPane());
        tabbedPane.addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                updateDateConditionLabel(getSelectedDateCondition());
            }
        });

        return tabbedPane;
    }

    /**
     * Update date condition label.
     */
    private void updateDateConditionLabel(IDateCondition dateCondition) {
        Debug.traceEnter(log, dateCondition);
        labelDateCondition.setText(dateCondition.toString());
    }

    /**
     * Gets the selected date condition.
     * 
     * @return the selected date condition
     */
    private IDateCondition getSelectedDateCondition() {
        return getSelectedDateConditionPane().getDateCondition();
    }

    /**
     * Gets the selected date condition pane.
     * 
     * @return the selected date condition pane
     */
    private DateConditionPane<?> getSelectedDateConditionPane() {
        return (DateConditionPane<?>) tabbedPane.getSelectedComponent();
    }

    /**
     * Adds the tab.
     * 
     * @param pane the pane
     */
    private void addTab(DateConditionPane<?> pane) {
        dateCondPanes.put(pane.getDateCondition().getClass(), pane);
        tabbedPane.add(pane.getTitle(), pane);
        pane.addDateConditionListener(dateConditionListener);
    }

    /**
     * Gets the date condition pane.
     * 
     * @param dateCondition the date condition
     * @return the date condition pane
     */
    private DateConditionPane<?> getDateConditionPane(
            IDateCondition dateCondition) {
        return dateCondPanes.get(dateCondition.getClass());
    }

    /**
     * Gets the containing dialog.
     * 
     * @return the containing dialog
     */
    private JJDialog getContainingDialog() {
        JJDialog result = null;
        JDialog jDialog = UIUtils.getContainingDialog(this);
        if (jDialog != null && jDialog instanceof JJDialog) {
            result = (JJDialog) jDialog;
        }
        return result;
    }

    /**
     * Creates the buttons panel.
     * 
     * @return the j panel
     */
    private JPanel createButtonsPanel() {
        JPanel result = new JPanel(new FlowLayout(FlowLayout.CENTER, UI.HGAP,
                UI.VGAP));
        // TODO Localize
        JButton applyButton = new JButton("Применить");
        JButton okButton = new JButton("OK");
        JButton cancelButton = new JButton("Отмена");

        result.add(applyButton);
        result.add(okButton);
        result.add(cancelButton);

        applyButton.addActionListener(new ActionListener() {

            /**
             * {@inheritDoc}
             */
            @Override
            public void actionPerformed(ActionEvent e) {
                Debug.traceEnter(log);
                apply();
            }
        });

        okButton.addActionListener(new ActionListener() {
            /**
             * {@inheritDoc}
             */
            @Override
            public void actionPerformed(ActionEvent e) {
                Debug.traceEnter(log);
                apply();
                JJDialog jjDialog = getContainingDialog();
                if (jjDialog != null) {
                    jjDialog.setModalResult(JJDialog.MODAL_RESULT_OK);
                }
            }
        });

        cancelButton.addActionListener(new ActionListener() {

            /**
             * {@inheritDoc}
             */
            @Override
            public void actionPerformed(ActionEvent e) {
                Debug.traceEnter(log);
                JJDialog jjDialog = getContainingDialog();
                if (jjDialog != null) {
                    jjDialog.close();
                }
            }
        });
        return result;
    }
    /**
     * Populate.
     */
    private void populate() {
        Debug.traceEnter(log);
        tabbedPane
                .setSelectedComponent(getDateConditionPane(getOperationRuleAction()
                        .getDateCondition()));

        getSelectedDateConditionPane().setDateCondition(
                getOperationRuleAction().getDateCondition());
        moneyPane.setMoney(getOperationRuleAction().getMoney());
        dateRange.setDateRange(getOperationRuleAction().getDateCondition()
                .getRange());
        updateDateConditionLabel(getOperationRuleAction().getDateCondition());
    }

    /**
     * Apply.
     */
    private void apply() {
        Debug.traceEnter(log);
        IDateCondition dc = getSelectedDateCondition();
        dc.getRange().assign(dateRange.getDateRange());
        getOperationRuleAction().setDateCondition(dc);
        getOperationRuleAction().setMoney(moneyPane.getMoney());
        OperationRule rule = getOperationRuleAction().getOperationRule();
        rule.getBudget().fireOperationRuleChangedEvent(rule);
    }

    /**
     * Gets the operation rule action.
     * 
     * @return the operation rule action
     */
    public OperationRuleAction getOperationRuleAction() {
        CoreUtils.assertNotNull(operationRuleAction, "operationRuleAction");
        return operationRuleAction;
    }

    /**
     * Sets the operation rule action.
     * 
     * @param operationRuleAction the new operation rule action
     */
    public void setOperationRuleAction(OperationRuleAction operationRuleAction) {
        Debug.traceEnter(log);
        this.operationRuleAction = operationRuleAction;
        populate();
    }

    /**
     * Gets the title.
     * 
     * @return the title
     */
    public String getTitle() {
        return getOperationRuleAction().getOperationRule().getId();
    }
}
